<template>
  <div class="news-view">
    <div class="news-list-nav">
      <router-link v-if="page > 1" :to="'/' + type + '/' + (page - 1)">&lt; prev</router-link>
      <a v-else class="disabled">&lt; prev</a>
      <span>{{ page }}/{{ maxPage }}</span>
      <router-link v-if="hasMore" :to="'/' + type + '/' + (page + 1)">more &gt;</router-link>
      <a v-else class="disabled">more &gt;</a>
    </div>
    <transition :name="transition">
      <div class="news-list" :key="displayedPage" v-if="displayedPage > 0">
        <transition-group tag="ul" name="item">
          <item v-for="item in displayedItems" :key="item.id" :item="item">
          </item>
        </transition-group>
      </div>
    </transition>
  </div>
</template>

<script>
import { watchList } from '../api'
import Item from '../components/Item.vue'

export default {
  name: 'item-list',

  components: {
    Item
  },

  props: {
    type: String
  },

  data () {
    return {
      transition: 'slide-right',
      displayedPage: Number(this.$route.params.page) || 1,
      // TODO:如果这里能拿到初始化的store中的数据，那就说明router.Ready钩子触发开始到返回app实例前
      // store中的数据初始化了（通过执行asyncData选项），即获取到了firebase中的数据
      // 然后后续才是执行组件的data初始化，不然这个位置应该只能拿到store中的默认数据
      displayedItems: this.$store.getters.activeItems

// 在 Vue 的服务器端渲染（SSR）中，组件的 data 函数和 asyncData 函数的执行顺序是这样的：
// 首先，当路由被解析（router.onReady）后，会找到所有匹配的路由组件，然后对每个组件调用其 asyncData 函数（如果存在）。asyncData 函数通常用于在服务器端获取数据并设置 store 的状态。这个过程是异步的，需要等待所有的 asyncData 函数执行完毕并返回 Promise。
// 当所有的 asyncData 函数执行完毕，store 的状态就被初始化了。然后，Vue 开始创建和初始化组件。在这个过程中，会首先调用 beforeCreate 钩子，然后执行 data 函数，最后调用 created 钩子。
// 所以，asyncData 函数的执行是在组件的 data 函数之前的。这意味着，当 data 函数执行时，store 的状态应该已经被 asyncData 函数初始化了，你可以在 data 函数中获取到 store 的初始化状态。
// 这也是为什么在服务器端渲染（SSR）中，我们可以在 data 函数中获取到 store 的初始化状态。因为在 data 函数执行前，asyncData 函数已经执行完毕，store 的状态已经被初始化了。
    }
  },

  computed: {
    page () {
      return Number(this.$route.params.page) || 1
    },
    maxPage () {
      const { itemsPerPage, lists } = this.$store.state
      return Math.ceil(lists[this.type].length / itemsPerPage)
    },
    hasMore () {
      return this.page < this.maxPage
    }
  },

  beforeMount () {
    // 当前根组件已经挂载的话，就加载数据
    if (this.$root._isMounted) {
      this.loadItems(this.page)
    }
    // watch the current list for realtime updates
    // 监听当前type类型列表的实时更新
    this.unwatchList = watchList(this.type, ids => {
      this.$store.commit('SET_LIST', { type: this.type, ids })
      this.$store.dispatch('ENSURE_ACTIVE_ITEMS').then(() => {
        this.displayedItems = this.$store.getters.activeItems
      })
    })
  },

  beforeDestroy () {
    this.unwatchList()
  },

  watch: {
    // 1. activeIds（getter）会改变，然后activeItems（getter）也会改变
    // 2. 下面调用loadItems，重新获取一遍当前type的id列表，重新更新当前活跃id列表
    //    然后更新活跃id列表对应的item列表
    page (to, from) {
      this.loadItems(to, from)
    }
  },

  methods: {
    loadItems (to = this.page, from = -1) {
      this.$bar.start()
      // 只要调用了FETCH_LIST_DATA(mutation)，那么就可以获得当前活跃type/活跃id列表下的
      // 最新的item列表
      this.$store.dispatch('FETCH_LIST_DATA', {
        type: this.type
      }).then(() => {
        if (this.page < 0 || this.page > this.maxPage) {
          this.$router.replace(`/${this.type}/1`)
          return
        }
        this.transition = from === -1 // 控制切页动画
          ? null
          : to > from ? 'slide-left' : 'slide-right'
        this.displayedPage = to
        this.displayedItems = this.$store.getters.activeItems
        this.$bar.finish()
      })
    }
  }
}
</script>

<style lang="stylus">
.news-view
  padding-top 45px

.news-list-nav, .news-list
  background-color #fff
  border-radius 2px

.news-list-nav
  padding 15px 30px
  position fixed
  text-align center
  top 55px
  left 0
  right 0
  z-index 998
  box-shadow 0 1px 2px rgba(0,0,0,.1)
  a
    margin 0 1em
  .disabled
    color #ccc

.news-list
  position absolute
  margin 30px 0
  width 100%
  transition all .5s cubic-bezier(.55,0,.1,1)
  ul
    list-style-type none
    padding 0
    margin 0

.slide-left-enter, .slide-right-leave-to
  opacity 0
  transform translate(30px, 0)

.slide-left-leave-to, .slide-right-enter
  opacity 0
  transform translate(-30px, 0)

.item-move, .item-enter-active, .item-leave-active
  transition all .5s cubic-bezier(.55,0,.1,1)

.item-enter
  opacity 0
  transform translate(30px, 0)

.item-leave-active
  position absolute
  opacity 0
  transform translate(30px, 0)

@media (max-width 600px)
  .news-list
    margin 10px 0
</style>
